{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import base64\n",
    "from __future__ import print_function # py 2.7 compat.\n",
    "import ipywidgets as widgets # Widget definitions.\n",
    "from traitlets import Unicode # Traitlet needed to add synced attributes to the widget."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a custom widget that allows the user to upload file data to the notebook server.  The file data is sent via a statefull `value` attribute of the widget.  The widget has an upload failed event that fires in the front-end and is echoed to the back-end using a custom msg."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class FileWidget(widgets.DOMWidget):\n",
    "    _view_name = Unicode('FilePickerView').tag(sync=True)\n",
    "    _view_module = Unicode('filepicker').tag(sync=True)\n",
    "\n",
    "    value = Unicode().tag(sync=True)\n",
    "    filename = Unicode().tag(sync=True)\n",
    "    \n",
    "    def __init__(self, **kwargs):\n",
    "        \"\"\"Constructor\"\"\"\n",
    "        widgets.DOMWidget.__init__(self, **kwargs) # Call the base.\n",
    "        \n",
    "        # Allow the user to register error callbacks with the following signatures:\n",
    "        #    callback()\n",
    "        #    callback(sender)\n",
    "        self.errors = widgets.CallbackDispatcher(accepted_nargs=[0, 1])\n",
    "        \n",
    "        # Listen for custom msgs\n",
    "        self.on_msg(self._handle_custom_msg)\n",
    "\n",
    "    def _handle_custom_msg(self, content):\n",
    "        \"\"\"Handle a msg from the front-end.\n",
    "\n",
    "        Parameters\n",
    "        ----------\n",
    "        content: dict\n",
    "            Content of the msg.\"\"\"\n",
    "        if 'event' in content and content['event'] == 'error':\n",
    "            self.errors()\n",
    "            self.errors(self)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "requirejs.undef('filepicker');\n",
       "\n",
       "define('filepicker', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "\n",
       "    var FilePickerView = widgets.DOMWidgetView.extend({\n",
       "\n",
       "        render: function() {\n",
       "            // Render the view.\n",
       "            this.setElement($('<input />')\n",
       "                .attr('type', 'file'));\n",
       "        },\n",
       "        \n",
       "        events: {\n",
       "            // List of events and their handlers.\n",
       "            'change': 'handle_file_change',\n",
       "        },\n",
       "       \n",
       "        handle_file_change: function(evt) { \n",
       "            // Handle when the user has changed the file.\n",
       "            \n",
       "            // Retrieve the first (and only!) File from the FileList object\n",
       "            var file = evt.target.files[0];\n",
       "            if (file) {\n",
       "\n",
       "                // Read the file's textual content and set value to those contents.\n",
       "                var that = this;\n",
       "                var file_reader = new FileReader();\n",
       "                file_reader.onload = function(e) {\n",
       "                    that.model.set('value', e.target.result);\n",
       "                    that.touch();\n",
       "                }\n",
       "                file_reader.readAsText(file);\n",
       "            } else {\n",
       "\n",
       "                // The file couldn't be opened.  Send an error msg to the\n",
       "                // back-end.\n",
       "                this.send({ 'event': 'error' });\n",
       "            }\n",
       "\n",
       "            // Set the filename of the file.\n",
       "            this.model.set('filename', file.name);\n",
       "            this.touch();\n",
       "        },\n",
       "    });\n",
       "    \n",
       "    return {\n",
       "        FilePickerView: FilePickerView\n",
       "    };  \n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "\n",
    "requirejs.undef('filepicker');\n",
    "\n",
    "define('filepicker', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "\n",
    "    var FilePickerView = widgets.DOMWidgetView.extend({\n",
    "\n",
    "        render: function() {\n",
    "            // Render the view.\n",
    "            this.setElement($('<input />')\n",
    "                .attr('type', 'file'));\n",
    "        },\n",
    "        \n",
    "        events: {\n",
    "            // List of events and their handlers.\n",
    "            'change': 'handle_file_change',\n",
    "        },\n",
    "       \n",
    "        handle_file_change: function(evt) { \n",
    "            // Handle when the user has changed the file.\n",
    "            \n",
    "            // Retrieve the first (and only!) File from the FileList object\n",
    "            var file = evt.target.files[0];\n",
    "            if (file) {\n",
    "\n",
    "                // Read the file's textual content and set value to those contents.\n",
    "                var that = this;\n",
    "                var file_reader = new FileReader();\n",
    "                file_reader.onload = function(e) {\n",
    "                    that.model.set('value', e.target.result);\n",
    "                    that.touch();\n",
    "                }\n",
    "                file_reader.readAsText(file);\n",
    "            } else {\n",
    "\n",
    "                // The file couldn't be opened.  Send an error msg to the\n",
    "                // back-end.\n",
    "                this.send({ 'event': 'error' });\n",
    "            }\n",
    "\n",
    "            // Set the filename of the file.\n",
    "            this.model.set('filename', file.name);\n",
    "            this.touch();\n",
    "        },\n",
    "    });\n",
    "    \n",
    "    return {\n",
    "        FilePickerView: FilePickerView\n",
    "    };  \n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following shows how the file widget can be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAAhCAYAAAAmqtMAAAACmklEQVR4Xu3XMREAAAgDMerfNCZ+DAI65Fh+5wgQIECAAAECBAgQIECAAIFMYNmSIQIECBAgQIAAAQIECBAgQOCEticgQIAAAQIECBAgQIAAAQKhgNAOMU0RIECAAAECBAgQIECAAAGh7QcIECBAgAABAgQIECBAgEAoILRDTFMECBAgQIAAAQIECBAgQEBo+wECBAgQIECAAAECBAgQIBAKCO0Q0xQBAgQIECBAgAABAgQIEBDafoAAAQIECBAgQIAAAQIECIQCQjvENEWAAAECBAgQIECAAAECBIS2HyBAgAABAgQIECBAgAABAqGA0A4xTREgQIAAAQIECBAgQIAAAaHtBwgQIECAAAECBAgQIECAQCggtENMUwQIECBAgAABAgQIECBAQGj7AQIECBAgQIAAAQIECBAgEAoI7RDTFAECBAgQIECAAAECBAgQENp+gAABAgQIECBAgAABAgQIhAJCO8Q0RYAAAQIECBAgQIAAAQIEhLYfIECAAAECBAgQIECAAAECoYDQDjFNESBAgAABAgQIECBAgAABoe0HCBAgQIAAAQIECBAgQIBAKCC0Q0xTBAgQIECAAAECBAgQIEBAaPsBAgQIECBAgAABAgQIECAQCgjtENMUAQIECBAgQIAAAQIECBAQ2n6AAAECBAgQIECAAAECBAiEAkI7xDRFgAABAgQIECBAgAABAgSEth8gQIAAAQIECBAgQIAAAQKhgNAOMU0RIECAAAECBAgQIECAAAGh7QcIECBAgAABAgQIECBAgEAoILRDTFMECBAgQIAAAQIECBAgQEBo+wECBAgQIECAAAECBAgQIBAKCO0Q0xQBAgQIECBAgAABAgQIEBDafoAAAQIECBAgQIAAAQIECIQCD8OvACLCKoC0AAAAAElFTkSuQmCC",
      "text/html": [
       "<img src= class=\"jupyter-widget\">\n",
       "<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"FilePickerView\",\"_view_module\":\"filepicker\",\"layout\":\"IPY_MODEL_8edac25958c440afb02c4c6a02ea979c\",\"filename\":\"\",\"_model_name\":\"DOMWidgetModel\",\"value\":\"\"}]</script>"
      ]
     },
     "metadata": {
      "isWidgetSnapshot": true
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "file_widget = FileWidget()\n",
    "\n",
    "# Register an event to echo the filename when it has been changed.\n",
    "def file_loading(change):\n",
    "    print(\"Loading %s\" % file_widget.filename)\n",
    "file_widget.observe(file_loading, names='filename')\n",
    "\n",
    "# Register an event to echo the filename and contents when a file\n",
    "# has been uploaded.\n",
    "def file_loaded(change):\n",
    "    print(\"Loaded, file contents: %s\" % file_widget.value)\n",
    "file_widget.observe(file_loaded, names='value')\n",
    "\n",
    "# Register an event to print an error message when a file could not\n",
    "# be opened.  Since the error messages are not handled through\n",
    "# traitlets but instead handled through custom msgs, the registration\n",
    "# of the handler is different than the two examples above.  Instead\n",
    "# the API provided by the CallbackDispatcher must be used.\n",
    "def file_failed():\n",
    "    print(\"Could not load file contents of %s\" % file_widget.filename)\n",
    "file_widget.errors.register_callback(file_failed)\n",
    "\n",
    "file_widget"
   ]
  }
 ],
 "metadata": {
  "cell_tags": [
   [
    "<None>",
    null
   ]
  ],
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  },
  "widgets": {
   "state": {
    "8edac25958c440afb02c4c6a02ea979c": {
     "views": []
    },
    "e33ff02b6b56431f862f7a80c388fa26": {
     "views": [
      {
       "cell": {
        "cell_type": "code",
        "execution_count": 4,
        "metadata": {
         "collapsed": false,
         "trusted": true
        },
        "outputs": [
         {
          "data": {
           "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAAhCAYAAAAmqtMAAAACmklEQVR4Xu3XMREAAAgDMerfNCZ+DAI65Fh+5wgQIECAAAECBAgQIECAAIFMYNmSIQIECBAgQIAAAQIECBAgQOCEticgQIAAAQIECBAgQIAAAQKhgNAOMU0RIECAAAECBAgQIECAAAGh7QcIECBAgAABAgQIECBAgEAoILRDTFMECBAgQIAAAQIECBAgQEBo+wECBAgQIECAAAECBAgQIBAKCO0Q0xQBAgQIECBAgAABAgQIEBDafoAAAQIECBAgQIAAAQIECIQCQjvENEWAAAECBAgQIECAAAECBIS2HyBAgAABAgQIECBAgAABAqGA0A4xTREgQIAAAQIECBAgQIAAAaHtBwgQIECAAAECBAgQIECAQCggtENMUwQIECBAgAABAgQIECBAQGj7AQIECBAgQIAAAQIECBAgEAoI7RDTFAECBAgQIECAAAECBAgQENp+gAABAgQIECBAgAABAgQIhAJCO8Q0RYAAAQIECBAgQIAAAQIEhLYfIECAAAECBAgQIECAAAECoYDQDjFNESBAgAABAgQIECBAgAABoe0HCBAgQIAAAQIECBAgQIBAKCC0Q0xTBAgQIECAAAECBAgQIEBAaPsBAgQIECBAgAABAgQIECAQCgjtENMUAQIECBAgQIAAAQIECBAQ2n6AAAECBAgQIECAAAECBAiEAkI7xDRFgAABAgQIECBAgAABAgSEth8gQIAAAQIECBAgQIAAAQKhgNAOMU0RIECAAAECBAgQIECAAAGh7QcIECBAgAABAgQIECBAgEAoILRDTFMECBAgQIAAAQIECBAgQEBo+wECBAgQIECAAAECBAgQIBAKCO0Q0xQBAgQIECBAgAABAgQIEBDafoAAAQIECBAgQIAAAQIECIQCD8OvACLCKoC0AAAAAElFTkSuQmCC",
           "text/html": "<img src= class=\"jupyter-widget\">\n<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"FilePickerView\",\"_view_module\":\"filepicker\",\"layout\":\"IPY_MODEL_8edac25958c440afb02c4c6a02ea979c\",\"filename\":\"\",\"_model_name\":\"DOMWidgetModel\",\"value\":\"\"}]</script>"
          },
          "metadata": {
           "isWidgetSnapshot": true
          },
          "output_type": "display_data"
         }
        ],
        "source": "file_widget = FileWidget()\n\n# Register an event to echo the filename when it has been changed.\ndef file_loading(change):\n    print(\"Loading %s\" % file_widget.filename)\nfile_widget.observe(file_loading, names='filename')\n\n# Register an event to echo the filename and contents when a file\n# has been uploaded.\ndef file_loaded(change):\n    print(\"Loaded, file contents: %s\" % file_widget.value)\nfile_widget.observe(file_loaded, names='value')\n\n# Register an event to print an error message when a file could not\n# be opened.  Since the error messages are not handled through\n# traitlets but instead handled through custom msgs, the registration\n# of the handler is different than the two examples above.  Instead\n# the API provided by the CallbackDispatcher must be used.\ndef file_failed():\n    print(\"Could not load file contents of %s\" % file_widget.filename)\nfile_widget.errors.register_callback(file_failed)\n\nfile_widget"
       },
       "cell_index": 5,
       "root": true
      }
     ]
    }
   },
   "version": "0.0.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
